/* 
 * File:   main.c
 * Author: Luís Costa
 *
 * Created on 25 de Abril de 2012, 15:40
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define BUF_SIZE_STDIN 1024		//Máximo buffer stdin
#define PASSWORD_SIZE 12		//Máximo password
#define USERNAME_SIZE 15		//Máximo username
#define NIF_SIZE 9 				//Máximo NIF
#define TELF_SIZE 9 			//Máximo para o contacto
#define DADOS_SIZE 256 			//Máximo de caracteres dos dados de cliente 

#define TRUE 1					//TRUE = 1 - simular boolean
#define FALSE 0					//FALSE = 0 - simular boolean
#define LEITURA 2				//LEITURA = 2 - definir constante leitura = 2
#define ESCRITA 3				//ESCRITA = 3 - definir constante escrita = 3

/*
 * 
 * Estruturas
 * 
 */
/* Estrutura usada para armazenar os dados de login */
typedef struct login {
	char username[USERNAME_SIZE + 1];
	char password[PASSWORD_SIZE + 1];
} Login;

/* Estrutura usada para armazenar os dados do cliente */
typedef struct cliente {
	char *nome;
	char *contacto;
	char *responsavel;
	char *nif;
	char *area;
	char *morada;
} Cliente;

/* Estrutura usada para armazenar o caminho dos directorios de escrita e leitura */
typedef struct directorio {
   char *leitura;
   char *escrita;
} Directorio;

/* Estrutura usada para armazenar os dados importantes nas várias partes do programa */
typedef struct globalSettings {
	char *log;
	char *configFile;
	char *usersFile;
	Directorio *directorio;
} GlobalSettings;


/*
 * Funções
 * 
 */
void startMenu(GlobalSettings *);
GlobalSettings *initGlobalSettings();
void flushStdin();
char *readStdin();
int validateInputLength(char *, int, int);
int validateMinLength(char *, int);
void menuConfiguracoes(GlobalSettings *);
int efectuaLogin(GlobalSettings *);
void menuPrivado(GlobalSettings *);
void carregarConfiguracoes(GlobalSettings *);
int validaLogin(Login, GlobalSettings *);
Directorio *readConfigFile(char *);
int criarUtilizador(GlobalSettings *);
Cliente *carregarCliente(GlobalSettings *);
Cliente *novoCliente(GlobalSettings *);
void listarCliente(Cliente *,GlobalSettings *);
int saveCliente(Cliente *,GlobalSettings *);
char *generatePath(char *, GlobalSettings *, int);
char *readLine(int);
int validateUniqueUser(char *, GlobalSettings *);
int saveUtilizador(Login, GlobalSettings *);
int validateDigits(char *);


/*
 * Ler ficheiro de Configurações
 * Formato:
 * ## Configurações
 * #rpath <directório de leitura>
 * #wpath <directório de escrita>
 * ##
 * name: readConfigFile
 * @param ficheiro
 * @return estrutura do tipo directorio
 * 
 */
Directorio *readConfigFile(char *file) {
    
    Directorio *directorio = NULL;
    
    int fd;
    struct stat statb;
    
    char *line = NULL;
    char *path = NULL;
    	
    char *rpath = NULL;	
    char *wpath = NULL;	
			
	//Abrir ficheiro em modo leitura e validar estado da abertura		
    if((fd = open(file, O_RDONLY)) < 0) {
        perror(file);
        return NULL;
    }
	
    //Obtém estrutura de dados com informação sobre o ficheiro
    if (fstat(fd, &statb) < 0) {
        perror(file);
        return NULL;
    }
    
    
    //O objectivo aqui é validar se é um ficheiro regular ou link
    //A função não tem o comportamento desejado
    //com lstat já funciona, mas não aceita o file descriptor, mas sim o nome do ficheiro
    // que pode originar corridas do tipo TOCTOU
    //if (!(statb.st_mode & S_IFREG)) {
		//return NULL;
	//}
   

	//Ler linha a linha até ao fim do ficheiro
	//a linha já vem sem o caracter '\n'
    while ((line = readLine(fd)) != NULL) {		
		path = (char *) realloc (NULL, strlen(line) * sizeof(char));
		if(path != NULL) {
			if (sscanf(line, "rpath %s", path) == 1) { //se encontrar rpath com outro valor separado por espaço, guarda o valor
				rpath = path; // so guarda se for encontrado e atribuido a path
			} else if(sscanf(line, "wpath %s", path) == 1) {
				wpath = path;
			} else { }
		} 
    }
    close(fd); //fechar o filedescriptor
    
    if(rpath != NULL && wpath != NULL) {
		directorio = (Directorio *)malloc(sizeof(Directorio));
		if(directorio != NULL) {
			directorio->leitura = rpath;
			directorio->escrita = wpath;
		}
	}
    
    return directorio;
}


/*
 * Guardar um cliente em ficheiro
 * name: saveCliente
 * @param *cliente, *globalSettings
 * @return
 * 
 */
int saveCliente(Cliente *cliente, GlobalSettings *globalSettings) {
	
	if(cliente == NULL) {
		globalSettings->log = "Nenhum cliente em memória\0";
		return FALSE;
	}
	
	char *inputText, *file;
	
	int fd;
    struct stat statb;
	
	printf("\n\t\t***************************\n");
	printf("\t\t**    Guardar Cliente    **\n");
	printf("\t\t***************************\n");
	
	printf("\t\t|| Ficheiro: ");
	inputText = readStdin();
	if(!validateMinLength(inputText, 1)) { //validar input, no minimo 1 caracter, no máximo é o máximo permitido pelo buffer stdin
		globalSettings->log = "Ficheiro Inválido\0";
		return FALSE;
	}
	
	file = generatePath(inputText, globalSettings, ESCRITA); //Gerar caminho para o ficheiro
	if(file == NULL) {
		globalSettings->log = "Ficheiro Inválido\0";
		return FALSE;
	}
	
	//abrir o ficheiro em modo escrita, criação, e trunc para inicializar o ficheiro a 0 bytes
    if((fd = open(file, O_RDWR|O_CREAT|O_SYNC|O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
        perror(file); //imprimir mensagem de erro caso falhe a abertura do ficheiro
        return FALSE;
    }
	
    //Obtém estrutura de dados com informação sobre o ficheiro
    if (fstat(fd, &statb) < 0) {
        perror(file);
        return FALSE;
    }
        
    //O objectivo aqui é validar se é um ficheiro regular ou link
	//A função não tem o comportamento desejado
	//com lstat já funciona, mas não aceita o file descriptor, mas sim o nome do ficheiro
	// que pode originar corridas do tipo TOCTOU
	//if (!(statb.st_mode & S_IFREG)) {
		//return NULL;
	//}    
        
	int maxLen = DADOS_SIZE*2;
    char str[maxLen]; 
    
    snprintf(str, maxLen, "nome:%s\n", cliente->nome);
    write(fd, str, strlen(str));
    
    snprintf(str, maxLen, "contacto:%s\n", cliente->contacto);
    write(fd, str, strlen(str));
    
    snprintf(str, maxLen, "responsavel:%s\n", cliente->responsavel);
    write(fd, str, strlen(str));
    
    snprintf(str, maxLen, "nif:%s\n", cliente->nif);
    write(fd, str, strlen(str));
    
    snprintf(str, maxLen, "morada:%s\n", cliente->morada);
    write(fd, str, strlen(str));
    
    snprintf(str, maxLen, "area:%s\n", cliente->area);
    write(fd, str, strlen(str));
    
	close(fd);
	
	globalSettings->log = "Ficheiro Guardado\0";
	
	return TRUE;
}


/*
 * Guardar utilizador no ficheiro
 * name: saveUtilizador
 * @param loginData, globalSettings
 * @return TRUE / FALSE
 * 
 */
int saveUtilizador(Login loginData, GlobalSettings *globalSettings) {
	int fd;
    struct stat statb;
    
	char *file = generatePath(globalSettings->usersFile, globalSettings, LEITURA);  //gerar caminho
	if(file == NULL) {
		globalSettings->log = "Ficheiro Inválido\0";
		return FALSE;
	}

	//abrir em modo escrita, e append para acrescentar na lista de utilizadores ja criada
    if((fd = open(file, O_RDWR|O_APPEND|O_SYNC)) < 0) {
        perror(file);
        return FALSE;
    }
	
    //Obtém estrutura de dados com informação sobre o ficheiro
    if (fstat(fd, &statb) < 0) {
        perror(file);
        return FALSE;
    }
    
    //O objectivo aqui é validar se é um ficheiro regular ou link
	//A função não tem o comportamento desejado
	//com lstat já funciona, mas não aceita o file descriptor, mas sim o nome do ficheiro
	// que pode originar corridas do tipo TOCTOU
	//if (!(statb.st_mode & S_IFREG)) {
		//return NULL;
	//}
    
    int maxLen = USERNAME_SIZE + 1 + PASSWORD_SIZE; //username:password
    char str[maxLen + 2]; //username:password\n\0
    snprintf(str, maxLen + 2, "%s:%s\n", loginData.username, loginData.password);
    
    write(fd, str, strlen(str));
    
	close(fd);
	
	globalSettings->log = "Utilizador adicionado com sucesso\0";
	return TRUE;
}


/*
 * Ler ficheiro linha a linha
 * Lê da posição actual referenciada pelo file descriptor até encontrar fim de linha '\n'
 * ou fim do ficheiro
 * name: readLine
 * @param
 * @return linha lida, ou NULL em caso de fim de ficheiro
 * 
 */
char *readLine(int fd) {
	unsigned char c;
	char *line = NULL;
	int count = 0;
	ssize_t bytes_read;
    while((bytes_read = read(fd, &c, sizeof(c)) > 0) && c != '\n'){
		if(bytes_read != 0) {
			line = (char *) realloc (line, (count + 1) * sizeof(c));
			if(line != NULL) {
				line[count] = c; 
				count++;
			}
        }
	}
	if(count == 0 && c == '\n') { //caso encontre linhas vazias não retornar NULL para continuar a leitura
		line = (char *) realloc (line, (count + 1) * sizeof(c));
	}
	if(line != NULL) line[count] = '\0';
	return line;
}


/*
 * Validar login de utilizador
 * name: validaLogin
 * @param loginData, *globalSettings
 * @return TRUE / FALSE - login valido ou inválido
 * 
 */
int validaLogin(Login loginData, GlobalSettings *globalSettings) {
    
    int isValidLogin = FALSE;
    
    int fd;
    struct stat statb;
    
    char *line = NULL;
    char *username = NULL;
    char *password = NULL;
	
	char *file = generatePath(globalSettings->usersFile, globalSettings, LEITURA);	//gerar caminho para o ficheiro utilizadores	
    if(file == NULL) {
		globalSettings->log = "Ficheiro Inválido\0";
		return FALSE;
	}
	
	//abrir ficheiro em modo leitura apenas
    if((fd = open(file, O_RDONLY)) < 0) {
        perror(file);
        return isValidLogin;
    }
	
    //Obtém estrutura de dados com informação sobre o ficheiro
    if (fstat(fd, &statb) < 0) {
        perror(file);
        return isValidLogin;
    }   
    
    //O objectivo aqui é validar se é um ficheiro regular ou link
	//A função não tem o comportamento desejado
	//com lstat já funciona, mas não aceita o file descriptor, mas sim o nome do ficheiro
	// que pode originar corridas do tipo TOCTOU
	//if (!(statb.st_mode & S_IFREG)) {
		//return NULL;
	//}
    
    while (!isValidLogin && (line = readLine(fd)) != NULL) {
		username = (char *) malloc (strlen(line) * sizeof(char));
		password = (char *) malloc (strlen(line) * sizeof(char));
		
		if(username != NULL && password != NULL) {
			if (sscanf(line, "%[a-zA-Z1-9]%*[:]%[a-zA-Z1-9]", username, password) == 2) { //Lista de caracteres validos, letras e numeros
				int usernameLenght = strlen(username);
				int passwordLenght = strlen(password);
				int total = usernameLenght + passwordLenght;
				int totalLinha = strlen(line) - 1;
				if((strlen(username) <= USERNAME_SIZE) && (strlen(password) <= PASSWORD_SIZE) && total == totalLinha) {
					int cmp1 = strncmp(username, loginData.username, USERNAME_SIZE);
					int cmp2 = strncmp(password, loginData.password, PASSWORD_SIZE);
					if( cmp1 == 0 && cmp2 == 0 ) {
						isValidLogin = TRUE;
					} 
				} 
			} 
			free(username);
			free(password);
		} 
    }
    close(fd);
	return isValidLogin;
}


/*
 * Validar string para verificar se contem unicamente numeros
 * util para validar telefone, nif
 * name: validateDigits
 * @param str a validar
 * @return TRUE / FALSE
 * 
 */
int validateDigits(char *str) {
	
	if(str == NULL)
		return FALSE;
	
	char tmp[strlen(str)+1];	
	if (sscanf(str, "%[1-9]", tmp) == 1) { //Só aceita numeros
		return TRUE;
	}
	return FALSE;
}


/*
 * Validar string para conter no minimo minLenght caracteres
 * name: validateMinLength
 * @param
 * @return TRUE / FALSE
 * 
 */
int validateMinLength(char *str, int minLenght) {
	
	if(str != NULL) {
		int strLenght = strlen(str);
		if(strLenght >= minLenght) {
			return TRUE;
		}
	}
    return FALSE;
}


/*
 * Verificar se o utilziador já existe no ficheiro.
 * Se ja existir retorna user invalido False, cancela a criaçao do utilizador
 * name: validateUniqueUser
 * @param username, globalSettings
 * @return TRUE / FALSE
 * 
 */
int validateUniqueUser(char *username, GlobalSettings *globalSettings) {
	int isValidUser = TRUE;
    
    int fd;
    struct stat statb;
    
    char *line = NULL;
    char *userTMP = NULL;
	
	char *file = generatePath(globalSettings->usersFile, globalSettings, LEITURA);
	if(file == NULL) {
		globalSettings->log = "Ficheiro Inválido\0";
		return FALSE;
	}
	
    if((fd = open(file, O_RDONLY)) < 0) {
        perror(file);
        return FALSE;
    }
	
    //Obtém estrutura de dados com informação sobre o ficheiro
    if (fstat(fd, &statb) < 0) {
        perror(file);
        return FALSE;
    }   
    	
    //O objectivo aqui é validar se é um ficheiro regular ou link
	//A função não tem o comportamento desejado
	//com lstat já funciona, mas não aceita o file descriptor, mas sim o nome do ficheiro
	// que pode originar corridas do tipo TOCTOU
	//if (!(statb.st_mode & S_IFREG)) {
		//return NULL;
	//}
	
    while (isValidUser && (line = readLine(fd)) != NULL) {
		userTMP = (char *) malloc (strlen(line) * sizeof(char));
		if(userTMP != NULL) {
			if (sscanf(line, "%[a-zA-Z1-9]%*[:]%*s", userTMP) == 1) {
				int cmp1 = strncmp(userTMP, username, USERNAME_SIZE);
				if( cmp1 == 0 ) {
					isValidUser = FALSE;
				} 
			} 
			free(userTMP);
		} 
    }
    close(fd);
	return isValidUser;
}


/*
 * Lista os dados de um cliente
 * name: listarCliente
 * @param *cliente, *globalSettings
 * @return
 * 
 */
void listarCliente(Cliente *cliente, GlobalSettings *globalSettings) {
	
	if(cliente == NULL) {
		globalSettings->log = "Nenhum cliente em memória\0";
		return;
	}
		
	printf("\n\t*******************\n");
	printf("\t**    Cliente    **\n");
	printf("\t*******************\n");
	printf("\t||        Nome: %s\n", cliente->nome);
	printf("\t||    Contacto: %s\n", cliente->contacto);
	printf("\t|| Responsável: %s\n", cliente->responsavel);
	printf("\t||         NIF: %s\n", cliente->nif);
	printf("\t||      Morada: %s\n", cliente->morada);
	printf("\t||        Área: %s\n", cliente->area);
	printf("\n\n");
	
	globalSettings->log = "Cliente listado com sucesso\0";

}


/*
 * Criar novo cliente para memória
 * todos os inputs são validados para os valores das constantes definidas para cada campo
 * name: novoCliente
 * @param globalSettings
 * @return novo cliente criado ou NULL
 * 
 */
Cliente *novoCliente(GlobalSettings *globalSettings) {
	
	Cliente *cliente = NULL;
	cliente = (Cliente *)malloc(sizeof(Cliente));
	if(cliente == NULL) {
		globalSettings->log = "Erro ao criar cliente\0";
		return NULL;
	}
	
	char *inputText;
	
	printf("\n\t*******************\n");
	printf("\t**    Cliente    **\n");
	printf("\t*******************\n");
	printf("\t|| Nome (max. %d caracteres): ", DADOS_SIZE); inputText = readStdin();
	if(!validateInputLength(inputText, 1, DADOS_SIZE)) {
		globalSettings->log = "Nome Inválido\0";
		free(cliente); cliente = NULL;
		return cliente;
	}
	cliente->nome = inputText;
	
	printf("\t|| Contacto: (%d digitos): ",TELF_SIZE); inputText = readStdin();
	if(!validateInputLength(inputText, TELF_SIZE, TELF_SIZE) || !validateDigits(inputText)) {
		globalSettings->log = "Contacto Inválido\0";
		free(cliente); cliente = NULL;
		return cliente;
	}
	cliente->contacto = inputText;
	
	
	printf("\t|| Responsável (max. %d caracteres): ", DADOS_SIZE); inputText = readStdin();
	if(!validateInputLength(inputText, 1, DADOS_SIZE)) {
		globalSettings->log = "Responsável Inválido\0";
		free(cliente); cliente = NULL;
		return cliente;
	}
	cliente->responsavel = inputText;
	
	printf("\t|| NIF (%d digitos): ", NIF_SIZE); inputText = readStdin();
	if(!validateInputLength(inputText, NIF_SIZE, NIF_SIZE) || !validateDigits(inputText)) {
		globalSettings->log = "NIF Inválido\0";
		free(cliente); cliente = NULL;
		return cliente;
	}
	cliente->nif = inputText;
	
	printf("\t|| Morada (max. %d caracteres): ", DADOS_SIZE); inputText = readStdin();
	if(!validateInputLength(inputText, 1, DADOS_SIZE)) {
		globalSettings->log = "Morada Inválida\0";
		free(cliente); cliente = NULL;
		return cliente;
	}
	cliente->morada = inputText;
	
	printf("\t|| Área (max. %d caracteres): ", DADOS_SIZE); inputText = readStdin();
	if(!validateInputLength(inputText, 1, DADOS_SIZE)) {
		globalSettings->log = "Área Inválida\0";
		free(cliente); cliente = NULL;
		return cliente;
	}
	cliente->area = inputText;
	
	globalSettings->log = "Cliente lido com sucesso\0";
	
	return cliente;
}


/*
 * Gerar caminho absoluto ou relativo de acordo o ficheiro e directorios introduzidos.
 * 
 * name: generatePath
 * @param *file: ficheiro que se pretende gerar o caminho
 * @param *globalSettings: variaveis de configurações
 * @param  action: Leitura / Escrita
 * @return Apontador para o caminho gerado ou NULL em caso de erro
 * 
 */
char *generatePath(char *file, GlobalSettings *globalSettings, int action) {
	
	char *path = NULL;
	int size;
	
	if(action == LEITURA) { //Gerar caminho de leitura
		size = strlen(globalSettings->directorio->leitura); 
		if(size == 0) {
			globalSettings->log = "Directorio de leitura não definido\0";
			return NULL;
		}
		path = (char *) realloc (NULL, size);
		strncpy(path, globalSettings->directorio->leitura, size);
		path[size] = '\0'; //Terminar string
		
	} else if (action == ESCRITA) { //Gerar caminho de escrita
		size = strlen(globalSettings->directorio->escrita);
		if(size == 0) {
			globalSettings->log = "Directorio de escrita não definido\0";
			return NULL;
		}
		path = (char *) realloc (NULL, size);
		strncpy(path, globalSettings->directorio->escrita, size);
		path[size] = '\0'; //Terminar string
	}
	
	if(path == NULL) {
		return NULL;
	}
	
	size = strlen(path);
	//Se o último caracter  é diferente '/' -> Adicionar o caracter '/'
	if(path[size-1] != '/') {
		path = (char *) realloc (path, size);
		path[size] = '/';
		path[size+1] = '\0';
	}
	
	
	char *resultado = NULL;
	if(file != NULL && file[0] == '/') { //Se começar por '/' assume-se como caminho absoluto
		size = strlen(file);
		resultado = file;
	} else { //caminho relativo. transformar string juntando o directorio e o ficheiro
		size = strlen(path) + strlen(file);
		
		resultado = (char *)malloc(size * sizeof(char *) + 1);
		snprintf(resultado, size + 1, "%s%s", path, file);
		resultado[size] = '\0';
	}

	return resultado;
}


/*
 * Criar um novo utilzidor
 * name: criarUtilizador
 * @param globalSettings
 * @return TRUE / FALSE
 * 
 */
int criarUtilizador(GlobalSettings *globalSettings) {
	
	char *inputText, tmp[USERNAME_SIZE+PASSWORD_SIZE];
	Login loginData;
	    
	printf("\n\t\t***************************\n");
	printf("\t\t**    Novo Utilizador    **\n");
	printf("\t\t***************************\n");	

	globalSettings->log = "\0";
	
	printf("\t\t|| Username (%d caracteres no máximo): ", USERNAME_SIZE);
	inputText = readStdin();
	//validar username
	if(!validateInputLength(inputText, 1, USERNAME_SIZE) || (sscanf(inputText, "%[a-zA-Z1-9]", tmp) != 1) || !validateUniqueUser(inputText, globalSettings)) {
		globalSettings->log = "Username inválido";
		return FALSE;
	} else {
		strncpy(loginData.username, inputText, strlen(inputText));
		loginData.username[strlen(inputText)] = '\0';
	}
	
	printf("\t\t|| Password (%d caracteres no máximo): ", PASSWORD_SIZE);
	inputText = readStdin();
	//validar password
	if(!validateInputLength(inputText, 1, PASSWORD_SIZE) || (sscanf(inputText, "%[a-zA-Z1-9]", tmp) != 1)) {
		globalSettings->log = "Password inválida";
		return FALSE;
	} else {
		strncpy(loginData.password, inputText, strlen(inputText));
		loginData.password[strlen(inputText)] = '\0';
	}
	
	/* Nas funçoes strncpy , tem de se terminar a string com '\0' para marcar fim de string */
	
	return saveUtilizador(loginData, globalSettings); //Guardar novo utilizador
}


/*
 * Definir o ficheiro de utilizadores
 * Formato:
 * #Ficheiro de utilizadores
 * #<username> <password>
 * name: definirUtilizadores
 * @param *globalSettings
 * @return
 * 
 */
void definirUtilizadores(GlobalSettings *globalSettings) {
	char *inputText;
	
	printf("\n\t\t************************\n");
	printf("\t\t**    Utilizadores    **\n");
	printf("\t\t************************\n");
	
	printf("\t\t|| Ficheiro: ");
	inputText = readStdin();
	if(!validateMinLength(inputText, 1)) {
		globalSettings->log = "Ficheiro Inválido\0";
		return;
	}
	
	globalSettings->usersFile = inputText;
	globalSettings->log = "Ficheiro utilizadores definido\0";
}


/*
 * Carregar um cliente do ficheiro
 * Formato
 * nome:<nome>
 * contacto:<telefone>
 * responsavel:<responsavel>
 * nif:<nif>
 * morada:<morada>
 * area:<area>
 * name: carregarCliente
 * @param *globalSettings
 * @return novo cliente carregado
 * 
 */
Cliente *carregarCliente(GlobalSettings *globalSettings) {
	
	char *inputText, *file;
	
	int fd;
    struct stat statb;
    
    Cliente *cliente = NULL;
	
	printf("\n\t\t****************************\n");
	printf("\t\t**    Carregar Cliente    **\n");
	printf("\t\t****************************\n");
	
	printf("\t\t|| Ficheiro: ");
	inputText = readStdin();
	if(!validateMinLength(inputText, 1)) {
		globalSettings->log = "Ficheiro Inválido\0";
		return FALSE;
	}
	
	file = generatePath(inputText, globalSettings, LEITURA);
	if(file == NULL) {
		globalSettings->log = "Ficheiro Inválido\0";
		return FALSE;
	}
	
	
	//Abrir em modo leitura apenas
	if((fd = open(file, O_RDONLY)) < 0) {
        perror(file);
        return NULL;
    }
	
    //Obtém estrutura de dados com informação sobre o ficheiro
    if (fstat(fd, &statb) < 0) {
        perror(file);
        return NULL;
    }   

	//O objectivo aqui é validar se é um ficheiro regular ou link
	//A função não tem o comportamento desejado
	//com lstat já funciona, mas não aceita o file descriptor, mas sim o nome do ficheiro
	// que pode originar corridas do tipo TOCTOU
	//if (!(statb.st_mode & S_IFREG)) {
		//return NULL;
	//}
	
	char *nome = NULL, *contacto = NULL, *responsavel = NULL, *nif = NULL, *morada = NULL, *area = NULL;
	char *str, *line;
    while ((line = readLine(fd)) != NULL) {
		str = (char *) realloc (NULL, strlen(line) * sizeof(char));
		if(str != NULL) {
			if (sscanf(line, "nome:%[^\t\n]", str) == 1) {
				nome = str;
			} else if(sscanf(line, "contacto:%s", str) == 1) {
				contacto = str;
			} else if(sscanf(line, "responsavel:%[^\t\n]", str) == 1) {
				responsavel = str;
			} else if(sscanf(line, "nif:%s", str) == 1) {
				nif = str;
			} else if(sscanf(line, "morada:%[^\t\n]", str) == 1) {
				morada = str;
			} else if(sscanf(line, "area:%[^\t\n]", str) == 1) {
				area = str;
			} else {}
		} 
    }
    
    close(fd);
    
    if(nome != NULL && contacto != NULL && responsavel != NULL && nif != NULL && morada != NULL && area != NULL) {
		cliente = (Cliente *)malloc(sizeof(Cliente));
		if(cliente != NULL) {
			cliente->nome = nome;
			cliente->contacto = contacto;
			cliente->responsavel = responsavel;
			cliente->nif = nif;
			cliente->morada = morada;
			cliente->area = area;
			
			globalSettings->log = "Cliente Carregado\0";
			return cliente;
		} 
	} 
    
    globalSettings->log = "Cliente inválido\0";
    return NULL;
}


/*
 * Menu para utilziadores registados
 * name: menuPrivado
 * @param
 * @return
 * 
 */
void menuPrivado(GlobalSettings *globalSettings) {
	char *inputText;
    int opcao = 0;
    Cliente *cliente = NULL, *tmp = NULL;
    
    do {
        printf("\n\t\t*******************\n");
        printf("\t\t**    M E N U    **\n");
        printf("\t\t*******************\n");
        printf("\t\t||   %s\n", globalSettings->log);
        printf("\t\t||____________________________________________\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t   1 - Criar Utilizador\t\t      ||\n");
        printf("\t\t||\t   2 - Carregar Cliente     \t      ||\n");
        printf("\t\t||\t   3 - Novo Cliente     \t      ||\n");
        printf("\t\t||\t   4 - Listar Cliente     \t      ||\n");
        printf("\t\t||\t   5 - Gravar Cliente     \t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t   S - VOLTAR\t\t\t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||____________________________________________||\n");
        printf("\t\t||\t\t\t\t   ||\n");
        printf("\t\t||_________________________________||  ");

        globalSettings->log = "\0";

        printf("Opcao: ");
        inputText = readStdin();
        if(validateInputLength(inputText, 1, 1)) { //um caracter apenas
            
            if (inputText[0] == 's' || inputText[0] == 'S') {
                opcao = -1;
            } else {
                opcao = atoi(inputText);
            }

            switch (opcao) {
                case 1: criarUtilizador(globalSettings);break;                   
                case 2: 
						tmp = carregarCliente(globalSettings); 
						if(tmp != NULL) {
							if(cliente != NULL) free(cliente);
							cliente = tmp;
						} 
						break;
                case 3: 
						tmp = novoCliente(globalSettings);
						if(tmp != NULL) {
							if(cliente != NULL) free(cliente);
							cliente = tmp;
						}
						break;                   
                case 4: listarCliente(cliente,globalSettings);break;
                case 5: saveCliente(cliente, globalSettings);break;
                case -1: break;    
                default: globalSettings->log = "Opção inválida"; break;
            }
            
        } else {
            globalSettings->log = "Opção inválida";
        }   

    } while (opcao != -1);
}


/*
 * 
 * name: carregarConfiguracoes
 * @param
 * @return
 * 
 */
void carregarConfiguracoes(GlobalSettings *globalSettings) {
	
	char *inputText;
	Directorio *directorio = NULL;
	
	printf("\n\t\t|| Caminho do ficheiro: ");
	inputText = readStdin();
	if(validateMinLength(inputText, 1)) {
		
		directorio = readConfigFile(inputText);
		
		if(directorio == NULL) {
			globalSettings->log = "Erro ao ler configurações";
		} else {
			globalSettings->directorio = directorio;
			globalSettings->log = "Definições carregadas com sucesso";
		}
	}
}


/*
 * 
 * name: flushStdin
 * 
 * Limpar o buffer do teclado.
 * Esta função é chamada quando for detectado na aplicação que foram 
 * inseridos mais caracteres que o permitido. 
 * Enquanto houver caracteres no stdin, esta função lê-os e descarta-os.
 * 
 * @return void
 * 
 */
void flushStdin() {
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF);
} 


/*
 * Ler inputs do teclado
 * Usa a função fgets e limita a BUF_SIZE_STDIN caracteres
 * Desta forma evita-se falhas de segurança do tipo buffer overflow
 * name: readStdin
 * @return apontador para o input lido
 * 
 */
char *readStdin() {
    char *buf;
    char *s;
    int chars_read = 0;

    buf = (char *)malloc(BUF_SIZE_STDIN * sizeof(char));
    if(buf == NULL) {
		perror("Erro leitura");
		return NULL;
	}
    
    s = fgets(buf, BUF_SIZE_STDIN, stdin);
    if(s == NULL) {
        perror("Erro leitura");
        return NULL;
    }
           
    /* Eliminar o caracter especial \n do valor lido do teclado */
    /* Se o ultimo caracter lido = \n substituir por \0 */
    chars_read = strlen(buf); 
    if(buf[chars_read - 1] == '\n') {
        buf[chars_read - 1] = '\0';
    } else {
		/* Limpar buffer teclado */
		/* \n ficou em buffer porque foram inseridos no minimo BUF_SIZE_STDIN caracteres (incluindo o \n ) */
		flushStdin();
    }    
    chars_read = strlen(buf); //Tamanho do input sem o '\n'
    
    if(chars_read == 0){
        return NULL;
    };
    
    return buf;
}


/*
 * Efectuar login
 * name: efectuaLogin
 * @return TRUE / FALSE - login com sucesso e login errado
 * 
 */
int efectuaLogin(GlobalSettings *globalSettings) {
	
	//Cancelar login se não estiverem definidas as variaveis de configuração
	if(strlen(globalSettings->usersFile) == 0 || strlen(globalSettings->directorio->leitura) == 0 || strlen(globalSettings->directorio->escrita) == 0) {
		globalSettings->log = "Configurações não definidas";
		return FALSE;
	}
	
	char *inputText;
	Login loginData;
	    
	printf("\n\t\t*****************\n");
	printf("\t\t**    Login    **\n");
	printf("\t\t*****************\n");
	printf("\t\t||   %s\n", globalSettings->log);
	printf("\t\t||____________________________________________\n");	

	globalSettings->log = "\0";

	printf("\t\t|| Username: (%d caracteres no máximo): ", USERNAME_SIZE);
	inputText = readStdin();
	//Validar input 1 caracter no minimo, USERNAME_SIZE no máximo
	//evitar o buffer overflow
	if(!validateInputLength(inputText, 1, USERNAME_SIZE)) { 
		globalSettings->log = "Username inválido";
		return FALSE;
	} else {
		strncpy(loginData.username, inputText, strlen(inputText)); //copiar 
		loginData.username[strlen(inputText)] = '\0'; //colocar fim de string
	}
	
	printf("\t\t|| Password: (%d caracteres no máximo): ", PASSWORD_SIZE);
	inputText = readStdin();
	//Validar input 1 caracter no minimo, PASSWORD_SIZE no máximo
	//evitar o buffer overflow
	if(!validateInputLength(inputText, 1, PASSWORD_SIZE)) {
		globalSettings->log = "Password inválida";
		return FALSE;
	} else {
		strncpy(loginData.password, inputText, strlen(inputText));
		loginData.password[strlen(inputText)] = '\0';
	}
        
    //validar login
    if(!validaLogin(loginData, globalSettings)) {
		globalSettings->log = "Login inválido";
		return FALSE;
	}

	char message[BUF_SIZE_STDIN];
	//The functions snprintf() and vsnprintf() write at most BUF_SIZE_STDIN size bytes 
	//(including the terminating null byte ('\0')) to str.
	if(snprintf(message, BUF_SIZE_STDIN, "Bem vindo: %s!", loginData.username) > 0) {
		globalSettings->log = message;
	}

    return TRUE;
}


/*
 * Verifica se uma string contem no minimo minLenght caracteres e no máximo maxLenght
 * name: validateInputLength
 * @param
 * @return TRUE / FALSE
 * 
 */
int validateInputLength(char *str, int minLenght, int maxLenght) {
	
	if(str != NULL) {
		int strLenght = strlen(str);
		if(strLenght >= minLenght && strLenght <= maxLenght) {
			return TRUE;
		}
	}
    return FALSE;
}


/*
 * Menu com opções de carregar ficheiro de configuração e utilizadores
 * name: menuConfiguracoes
 * @param globalSettings
 * @return
 * 
 */
void menuConfiguracoes(GlobalSettings *globalSettings) {
	
    char *inputText;
    int opcao = 0;
    
    do {
        printf("\n\t\t*************************\n");
        printf("\t\t**    Configurações    **\n");
        printf("\t\t*************************\n");
        printf("\t\t||   %s\n", globalSettings->log);
        printf("\t\t||____________________________________________\n");
        printf("\t\t|| Directório de leitura: %s\n", globalSettings->directorio->leitura);
        printf("\t\t|| Directório de escrita: %s\n", globalSettings->directorio->escrita);
        printf("\t\t|| Ficheiro Utilizadores: %s\n", globalSettings->usersFile);
        printf("\t\t||____________________________________________\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t   1 - Carregar configurações\t      ||\n");
        printf("\t\t||\t   2 - Carregar Utilizadores\t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t   S - VOLTAR\t\t\t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||____________________________________________||\n");
        printf("\t\t||\t\t\t\t   ||\n");
        printf("\t\t||_________________________________||  ");

        globalSettings->log = "\0";

        printf("Opcao: ");
        inputText = readStdin();
        if(validateInputLength(inputText, 1, 1)) { //Aceita somente 1 caracter
            
            if (inputText[0] == 's' || inputText[0] == 'S') {
                opcao = -1;
            } else {
                opcao = atoi(inputText); //Converter para inteiro
            }

            switch (opcao) {
                case 1: carregarConfiguracoes(globalSettings); break;                   
                case 2: definirUtilizadores(globalSettings); break;
                case -1: break;    
                default: globalSettings->log = "Opção inválida"; break;
            }
            
        } else {
            globalSettings->log = "Opção inválida";
        }   

    } while (opcao != -1);
    
}


/*
 * Instancia uma nova variavel para armazenar dados uteis nas diversas funções
 * -ficheiro de utilizadores
 * -directorias de escrita e leitura
 * name: initGlobalSettings
 * @param
 * @return GlobalSettings: apontador para globalsettings no estado inicial
 * 
 */
GlobalSettings *initGlobalSettings() {
	
	GlobalSettings *settings;

	settings = (GlobalSettings *)malloc(sizeof(GlobalSettings)); //alocar memoria
	if(settings == NULL) { //em caso de falha
		perror("Settings");
		return NULL;
	}
	
	settings->log = "\0"; //string vazia
	settings->configFile = "\0";
	settings->usersFile = "\0";
	
	settings->directorio = (Directorio *)malloc(sizeof(Directorio));//alocar memoria
	if(settings->directorio != NULL) {
		settings->directorio->leitura = "\0";
		settings->directorio->escrita = "\0";
	}
	
	return settings;
}


/*
 * Primeiro menu a ser aprentado
 * name: startMenu
 * @param
 * @return
 * 
 */
void startMenu(GlobalSettings *globalSettings) {
		
    char *inputText;
    int opcao = 0;
    
    do {
        printf("\n\t\t*******************\n");
        printf("\t\t**    M E N U    **\n");
        printf("\t\t*******************\n");
        printf("\t\t||   %s\n", globalSettings->log);
        printf("\t\t||____________________________________________\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t   1 - Configurar\t\t      ||\n");
        printf("\t\t||\t   2 - Login     \t\t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||\t   S - SAIR\t\t\t      ||\n");
        printf("\t\t||\t\t\t\t\t      ||\n");
        printf("\t\t||____________________________________________||\n");
        printf("\t\t||\t\t\t\t   ||\n");
        printf("\t\t||_________________________________||  ");

        globalSettings->log = "\0";

        printf("Opcao: ");
        inputText = readStdin();
        if(validateInputLength(inputText, 1, 1)) { //só aceita 1 caracter
            
            if (inputText[0] == 's' || inputText[0] == 'S') {
                opcao = -1;
            } else {
                opcao = atoi(inputText); //sconverter para inteiro
            }

            switch (opcao) {
                case 1: menuConfiguracoes(globalSettings); break;                   
                case 2: if(efectuaLogin(globalSettings)) 
							menuPrivado(globalSettings); 
						break;
                case -1: break;    
                default: globalSettings->log = "Opção inválida"; break;
            }
            
        } else {
            globalSettings->log = "Opção inválida";
        }   

    } while (opcao != -1);
}


/*
 * Inicio do programa
 */
int main(int argc, char** argv) {
	GlobalSettings *globalSettings = initGlobalSettings();
	startMenu(globalSettings);
	return (EXIT_SUCCESS);
}
